/**
  ******************************************************************************
  * @file    ring_queue.c
  * @author  ashuai0110
  * @version V2.1
  * @date    2023-09-24
  * @brief   环形队列
  *          (适合定长或易分离出的数据,写入后必须读出才可再写,写满后新数据无法写入)
  *
  ******************************************************************************
  * @attention
  *
  * 版权声明:内容为编者(ashuai0110)原创,使用请注明出处,当然,你也可以不这样做^_^
  * 出处链接:https://gitee.com/ashuai0110/mcu_reuse_development_module.git
  *
  ******************************************************************************
  */

/* 包含头文件-----------------------------------------------------------------*/
#include "ring_queue.h"

/**
 * @addtogroup modules
 * @{
 */

/**
 * @defgroup ring_queue ring_queue
 * @{
 */

/* 私有宏定义-----------------------------------------------------------------*/
/**
 * @defgroup ring_queue_local_macros ring queue local macros
 * @{
 */

/**
 * @defgroup ring_queue_check_parameters_validity ring queue check parameters validity
 * @{
 */
/* 环形队列数据存储区大小有效性检查 */
#define IS_VALID_RQ_SIZE(x)                                                   \
(       (x) != 0u)
/**
 * @}
 */

/**
 * @}
 */

/* 私有类型定义---------------------------------------------------------------*/

/* 私有变量-------------------------------------------------------------------*/

/* 全局变量-------------------------------------------------------------------*/

/* 私有函数原型---------------------------------------------------------------*/

/**
 * @defgroup ring_queue_global_functions ring queue global functions
 * @{
 */

/**
  * @brief  环形队列初始化
  *
  * @note   None
  *
  * @param  _rq  : 环形队列实例
  *
  * @param  pBuf : 数据存储区
  *
  * @param  size : 队列最大填充数量(数据存储区大小)
  *
  * @retval None
  */
void ring_queue_init(ring_queue_t *_rq, void *pBuf, uint32_t size)
{
    ASSERT_PARAM(IS_VALID_POINT(_rq));
    ASSERT_PARAM(IS_VALID_POINT(pBuf));
    ASSERT_PARAM(IS_VALID_RQ_SIZE(size));

    _rq->pBuf = (char *)pBuf;
    _rq->pHead = _rq->pBuf;
    _rq->pTail = _rq->pBuf;
    _rq->curCnt = 0;
    _rq->maxCnt = size;
}

/**
  * @brief  环形队列取消初始化
  *
  * @note   恢复默认
  *
  * @param  _rq : 环形队列实例
  *
  * @retval None
  */
void ring_queue_de_init(ring_queue_t *_rq)
{
    ASSERT_PARAM(IS_VALID_POINT(_rq));

    memset(_rq, 0, sizeof(ring_queue_t));
}

/**
  * @brief  获取环形队列的大小
  *
  * @note   缓冲区大小(byte)
  *
  * @param  _rq : 环形队列实例
  *
  * @retval 环形队列的大小
  */
uint32_t ring_queue_total_size(ring_queue_t *_rq)
{
    ASSERT_PARAM(IS_VALID_POINT(_rq));

    return _rq->maxCnt;
}

/**
  * @brief  获取环形队列的可读大小
  *
  * @note   待读出数量(byte)
  *
  * @param  _rq : 环形队列实例
  *
  * @retval 环形队列的可读大小
  */
uint32_t ring_queue_can_read(ring_queue_t *_rq)
{
    ASSERT_PARAM(IS_VALID_POINT(_rq));
    
    return _rq->curCnt;
}

/**
  * @brief  获取环形队列的可写大小
  *
  * @note   可写入数量(byte)
  *
  * @param  _rq : 环形队列实例
  *
  * @retval 环形队列的可写大小
  */
uint32_t ring_queue_can_write(ring_queue_t *_rq)
{
    ASSERT_PARAM(IS_VALID_POINT(_rq));
    
    return (_rq->maxCnt - _rq->curCnt);
}

/**
  * @brief  从环形队列读出数据
  *
  * @note   None
  *
  * @param  _rq   : 环形队列实例
  *
  * @param  pBuf  : 读出数据的存储区
  *
  * @param  size  : 读出数据大小
  *
  * @retval uint8_t
  *    @arg RET_OK  : 读出成功
  *    @arg RET_ERR : 参数size为0或超出可读大小
  */
uint8_t ring_queue_read(ring_queue_t *_rq, void *pBuf, uint32_t size)
{
    uint32_t readSize = 0;
    
    ASSERT_PARAM(IS_VALID_POINT(_rq));
    ASSERT_PARAM(IS_VALID_POINT(pBuf));
    
    if(0 == size || size > ring_queue_can_read(_rq))
    {
        return RET_ERR;
    }

    if(_rq->pHead < _rq->pTail)
    {
        memcpy(pBuf, _rq->pHead, size);
        _rq->pHead += size;
        _rq->curCnt -= size;
    }
    else
    {
        readSize = ring_queue_total_size(_rq) - (_rq->pHead - _rq->pBuf);
        if(size <= readSize)
        {
            memcpy(pBuf, _rq->pHead, size);
            _rq->pHead += size;
            _rq->curCnt -= size;
        }
        else
        {
            memcpy(pBuf, _rq->pHead, readSize);
            _rq->pHead = _rq->pBuf;
            _rq->curCnt -= readSize;
            ring_queue_read(_rq, (void *)((char *)pBuf + readSize), size - readSize);
        }
    }
    
    if(_rq->pHead >= (_rq->pBuf + ring_queue_total_size(_rq)))
    {
        _rq->pHead = _rq->pBuf;
    }

    return RET_OK;
}

/**
  * @brief  写入数据到环形队列
  *
  * @note   None
  *
  * @param  _rq   : 环形队列实例
  *
  * @param  pData : 写入数据的存储区
  *
  * @param  size  : 写入数据的大小
  *
  * @retval uint8_t
  *    @arg RET_OK  : 写入成功
  *    @arg RET_ERR : 参数size为0或超出可写大小
  */
uint8_t ring_queue_write(ring_queue_t *_rq, void *pData, uint32_t size)
{
    uint32_t writeSize = 0;
    
    ASSERT_PARAM(IS_VALID_POINT(_rq));
    ASSERT_PARAM(IS_VALID_POINT(pData));
    
    if(0 == size || size > ring_queue_can_write(_rq))
    {
        return RET_ERR;
    }

    if(_rq->pHead <= _rq->pTail)
    {
        writeSize = ring_queue_total_size(_rq) - (_rq->pTail - _rq->pBuf);
        if(size <= writeSize)
        {
            memcpy(_rq->pTail, pData, size);
            _rq->pTail += size;
            _rq->curCnt += size;
        }
        else
        {
            memcpy(_rq->pTail, pData, writeSize);
            _rq->pTail = _rq->pBuf;
            _rq->curCnt += writeSize;
            ring_queue_write(_rq, (void *)((char *)pData + writeSize), size - writeSize);
        }
    }
    else
    {
        memcpy(_rq->pTail, pData, size);
        _rq->pTail += size;
        _rq->curCnt += size;
    }
    
    if(_rq->pTail >= (_rq->pBuf + ring_queue_total_size(_rq)))
    {
        _rq->pTail = _rq->pBuf;
    }

    return RET_OK;
}

/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */
